/**
 * <p>Copyright (c) Snail Game 2014</p>
 */
package com.snail.billing.sso;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

/**
 * @Title: DefaultTicketRetriever.java
 * @Description:
 * @author Wang Junxiang
 * @date 2014-8-29
 * @version 1.0
 * @since 1.0
 */
public class DefaultTicketRetriever implements TicketRetriever {

	public static final int HTTP_CODE_TGT = 201;
	public static final int HTTP_CODE_ST = 200;

	private Connection connection;

	@Override
	public String getTicket(String ssoServer, String username, String password, String serviceUri) throws TicketRetrieveException {

		if (connection == null) {
			throw new IllegalArgumentException("Connection instance is required.");
		}

		String tgt = getTicketGrantingTicket(ssoServer, username, password);
		String st = getServiceTicket(ssoServer, tgt, serviceUri);
		return st;
	}

	@Override
	public String getTicketGrantingTicket(String ssoServer, String username, String password) throws TicketRetrieveException {

		if (connection == null) {
			throw new IllegalArgumentException("Connection instance is required.");
		}
		assertNotNull(ssoServer, "SSO server address is required");
		assertNotNull(username, "Username is required.");
		assertNotNull(password, "Password is required");

		try {
			// Request TGT code
			HttpResponse response = remoteCall(ssoServer, getTGTPostData(username, password));
			int statusCode = response.getStatusLine().getStatusCode();
			String responseBody = EntityUtils.toString(response.getEntity());

			// Process response
			switch (statusCode) {
				case HTTP_CODE_TGT: {
					final Matcher matcher = Pattern.compile(".*action=\".*/(.*?)\".*").matcher(responseBody);
					if (matcher.matches()) {
						return matcher.group(1);
					}
					throw new TicketRetrieveException("TGT request success, but no ticket retrieved.");
				}
				default:
					throw new TicketRetrieveException("Invalid response code: " + statusCode + " from SSO server!");
			}
		} catch (ClientProtocolException e) {
			throw new TicketRetrieveException("Failed to request TGT", e);
		} catch (IOException e) {
			throw new TicketRetrieveException("Failed to request TGT", e);
		}
	}

	@Override
	public String getServiceTicket(String ssoServer, String ticketGrantingTicket, String serviceUri) throws TicketRetrieveException {

		if (connection == null) {
			throw new IllegalArgumentException("Connection instance is required.");
		}
		assertNotNull(ssoServer, "SSO server address is required");
		assertNotNull(ticketGrantingTicket, "TGT is required");
		assertNotNull(serviceUri, "Service URI is required");

		try {
			// Request ST code
			String url = ssoServer + "/" + ticketGrantingTicket;
			HttpResponse response = remoteCall(url, getSTPostData(serviceUri));
			int statusCode = response.getStatusLine().getStatusCode();
			String responseBody = EntityUtils.toString(response.getEntity());

			// Process response
			switch (statusCode) {
				case HTTP_CODE_ST: {
					return responseBody;
				}
				default:
					throw new TicketRetrieveException("Invalid response code: " + statusCode + " from SSO server!");
			}
		} catch (ClientProtocolException e) {
			throw new TicketRetrieveException("Failed to request ST", e);
		} catch (IOException e) {
			throw new TicketRetrieveException("Failed to request ST", e);
		}
	}

	@Override
	public void setConnection(Connection connection) {
		if (connection == null) {
			throw new IllegalArgumentException("Connection instance is required.");
		}
		this.connection = connection;
	}

	protected HttpResponse remoteCall(String url, Map<String, String> data) throws ClientProtocolException, IOException {
		final HttpClient httpClient = connection.getConnection();
		final HttpPost post = new HttpPost(url);

		// Prepare request paremeters
		List<NameValuePair> postData = new ArrayList<NameValuePair>();
		for (Map.Entry<String, String> entry : data.entrySet()) {
			postData.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
		}

		try {
			// RMI
			UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(postData, "UTF-8");
			post.setEntity(uefEntity);
			HttpResponse response = httpClient.execute(post);
			return response;
		} finally {
			try {
				post.clone();
			} catch (CloneNotSupportedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// post.releaseConnection();
		}
	}

	protected void assertNotNull(String param, String message) {
		if (param == null || "".equals(param)) {
			throw new IllegalArgumentException(message);
		}
	}

	protected Map<String, String> getTGTPostData(String username, String password) {
		Map<String, String> tgtPostData = new HashMap<String, String>();
		tgtPostData.put(USERNAME, username);
		tgtPostData.put(PASSWORD, password);
		return tgtPostData;
	}

	protected Map<String, String> getSTPostData(String serviceUri) {
		Map<String, String> stPostData = new HashMap<String, String>();
		stPostData.put(SERVICE_URI, serviceUri);
		return stPostData;
	}

	public static void main(String[] args) throws ClientProtocolException, IOException {
		final String ssoServer = "https://sso.woniu.com/v1/tickets";
		final String username = "testcccc48";
		final String password = "111111";
		final String serviceUri = "http://test.woniu.com:8080/ssoc/";

		DefaultTicketRetriever ssoSdk = new DefaultTicketRetriever();
		ssoSdk.setConnection(HttpConnection.getInstance());
		// String st = ssoSdk.getTicket(ssoServer, username, password, serviceUri);

		String tgt = ssoSdk.getTicketGrantingTicket(ssoServer, username, password);
		String st = ssoSdk.getServiceTicket(ssoServer, tgt, serviceUri);
		System.out.println("TGT ... " + tgt);
		System.out.println("ST ... " + st);

		// TEST
		// HttpResponse response = ssoSdk.remoteCall(serviceUri + "?ticket=" + st, new HashMap<String, String>());
		// System.out.println(response.getStatusLine().getStatusCode());
		// System.out.println(EntityUtils.toString(response.getEntity()));
		//
		// st = ssoSdk.getServiceTicket(ssoServer, tgt, serviceUri);
		// System.out.println("ST ... " + st);
		// response = ssoSdk.remoteCall(serviceUri + "?ticket=" + st, new HashMap<String, String>());
		// System.out.println(response.getStatusLine().getStatusCode());
		// System.out.println(EntityUtils.toString(response.getEntity()));
		//
		// st = ssoSdk.getServiceTicket(ssoServer, tgt, serviceUri);
		// System.out.println("ST ... " + st);
		// response = ssoSdk.remoteCall(serviceUri + "?ticket=" + st, new HashMap<String, String>());
		// System.out.println(response.getStatusLine().getStatusCode());
		// System.out.println(EntityUtils.toString(response.getEntity()));
	}

}
